package me.dengfengdecao.demo.config.shiro;

import me.dengfengdecao.demo.component.cache.SpringCacheManagerWrapper;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.CachingSecurityManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Apache Shiro 核心通过 Filter 来实现, 要定义一系列关于URL的规则和访问权限。<br/>
 @see <a href=http://412887952-qq-com.iteye.com/blog/2299777>Spring Boot Shiro权限管理</a>
 @see <a href=http://shiro.apache.org/spring.html>Integrating Apache Shiro into Spring-based Applications</a>
  * <br/>Created by dengfengdecao on 16/9/29.
 */
@Configuration      // 如果注释掉这个注解的话,@cacheble就可以使用了。
public class ShiroConfiguration {

    /*
    缓存管理器 使用Redis实现
     */
    @Resource
    private SpringCacheManagerWrapper springCacheManagerWrapper;

    /**
     * ShiroFilterFactoryBean 处理拦截资源文件问题。<br/>
     * 注意：<br/>
     * 单独一个ShiroFilterFactoryBean配置是或报错的，因为在初始化ShiroFilterFactoryBean的时候需要注入：SecurityManager<br/>
     * Filter Chain定义说明:<br/>
     1、一个URL可以配置多个Filter，使用逗号分隔<br/>
     2、当设置多个过滤器时，全部验证通过，才视为通过<br/>
     3、部分过滤器可指定参数，如perms，roles>
     * @param securityManager
     * @return
     */
    @Bean(name="shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 必须设置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        System.out.println("ShiroConfiguration.shiroFilterFactoryBean : shiro实例化");

        // 拦截器
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();

        // 配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了
        filterChainDefinitionMap.put("/logout", "logout");

        // authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问
        filterChainDefinitionMap.put("/**", "authc");
        filterChainDefinitionMap.put("admin/**", "authc, roles[admin]");
        filterChainDefinitionMap.put("docs/**", "authc, perms[document:read]");

        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/index");
        // 未授权界面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return shiroFilterFactoryBean;
    }

    /**
     * shiro核心安全管理类
     * @return
     */
    @Bean
    public SecurityManager securityManager() {
        CachingSecurityManager securityManager = new DefaultWebSecurityManager(myShiroRealm());
        System.out.println("ShiroConfiguration.securityManager : 实例化");

        // 将myShiroRealm注入到securityManager中
        // securityManager.setRealm(myShiroRealm());
        // 将缓存对象注入到SecurityManager中
        // securityManager.setCacheManager(shiroRedisCacheManager);
        securityManager.setCacheManager(springCacheManagerWrapper);
        return securityManager;
    }

    @Bean
    public MyShiroRealm myShiroRealm() {
        MyShiroRealm myShiroRealm = new MyShiroRealm();
        // 注入凭证匹配器
        myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());

        System.out.println("ShiroConfiguration.myShiroRealm : 实例化");
        return myShiroRealm;
    }

    /**
     * 凭证匹配器
     * @return
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();

        System.out.println("ShiroConfiguration.hashedCredentialsMatcher : 实例化");
        // 加密算法
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        // 散列的次数，比如散列两次，相当于 md5(md5(""));
        hashedCredentialsMatcher.setHashIterations(2);

        return hashedCredentialsMatcher;
    }

    /**
     * 开启shiro aop注解支持.使用代理方式;所以需要开启代码支持;<br/>
     * 拦截@ RequiresPermissions注释的方法
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();

        System.out.println("ShiroConfiguration.authorizaionAttributeSourceAdvisor : 实例化");

        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);

        return authorizationAttributeSourceAdvisor;
    }
}
